{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# default_exp models.XCMPlus"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# XCM (An Explainable Convolutional Neural Network for Multivariate Time Series Classification)\n",
    "\n",
    "> This is an unofficial PyTorch implementation by Ignacio Oguiza of  - oguiza@gmail.com based on Temporal Convolutional Network (Bai, 2018).\n",
    "\n",
    "**References:**\n",
    "\n",
    "* Fauvel, K., Lin, T., Masson, V., Fromont, É., & Termier, A. (2020). XCM: An Explainable Convolutional Neural Network for Multivariate Time Series Classification. arXiv preprint arXiv:2009.04796.\n",
    "* Official XCM PyTorch implementation: not available as of Nov 27th, 2020"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#export\n",
    "from tsai.imports import *\n",
    "from tsai.utils import *\n",
    "from tsai.models.layers import *\n",
    "from tsai.models.utils import *\n",
    "from tsai.models.explainability import *"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#export\n",
    "# This is an unofficial PyTorch implementation by Ignacio Oguiza - oguiza@gmail.com based on:\n",
    "\n",
    "# Fauvel, K., Lin, T., Masson, V., Fromont, É., & Termier, A. (2020). XCM: An Explainable Convolutional Neural Network for\n",
    "# Multivariate Time Series Classification. arXiv preprint arXiv:2009.04796.\n",
    "# Official XCM PyTorch implementation: not available as of Nov 27th, 2020\n",
    "\n",
    "class XCMPlus(nn.Sequential):\n",
    "    def __init__(self, c_in:int, c_out:int, seq_len:Optional[int]=None, nf:int=128, window_perc:float=1., flatten:bool=False, custom_head:callable=None, \n",
    "                 concat_pool:bool=False, fc_dropout:float=0., bn:bool=False, y_range:tuple=None, **kwargs):\n",
    "        \n",
    "        window_size = int(round(seq_len * window_perc, 0))\n",
    "        \n",
    "        backbone = _XCMPlus_Backbone(c_in, c_out, seq_len=seq_len, nf=nf, window_perc=window_perc)\n",
    "            \n",
    "        self.head_nf = nf\n",
    "        self.c_out = c_out\n",
    "        self.seq_len = seq_len\n",
    "        if custom_head: head = custom_head(self.head_nf, c_out, seq_len, **kwargs)\n",
    "        else: head = self.create_head(self.head_nf, c_out, seq_len, flatten=flatten, concat_pool=concat_pool, \n",
    "                                           fc_dropout=fc_dropout, bn=bn, y_range=y_range)\n",
    "        \n",
    "        super().__init__(OrderedDict([('backbone', backbone), ('head', head)]))\n",
    "\n",
    "        \n",
    "    def create_head(self, nf, c_out, seq_len=None, flatten=False, concat_pool=False, fc_dropout=0., bn=False, y_range=None):\n",
    "        if flatten: \n",
    "            nf *= seq_len\n",
    "            layers = [Flatten()]\n",
    "        else: \n",
    "            if concat_pool: nf *= 2\n",
    "            layers = [GACP1d(1) if concat_pool else GAP1d(1)]\n",
    "        layers += [LinBnDrop(nf, c_out, bn=bn, p=fc_dropout)]\n",
    "        if y_range: layers += [SigmoidRange(*y_range)]\n",
    "        return nn.Sequential(*layers)\n",
    "    \n",
    "    \n",
    "    def show_gradcam(self, x, y=None, detach=True, cpu=True, apply_relu=True, cmap='inferno', figsize=None, **kwargs):\n",
    "        \n",
    "        att_maps = get_attribution_map(self, [self.backbone.conv2dblock, self.backbone.conv1dblock], x, y=y, detach=detach, cpu=cpu, apply_relu=apply_relu)\n",
    "        att_maps[0] = (att_maps[0] - att_maps[0].min()) / (att_maps[0].max() - att_maps[0].min())\n",
    "        att_maps[1] = (att_maps[1] - att_maps[1].min()) / (att_maps[1].max() - att_maps[1].min())\n",
    "\n",
    "        figsize = ifnone(figsize, (10, 10))\n",
    "        fig = plt.figure(figsize=figsize, **kwargs)\n",
    "        ax = plt.axes()\n",
    "        plt.title('Observed variables')\n",
    "        im = ax.imshow(att_maps[0], cmap=cmap)\n",
    "        cax = fig.add_axes([ax.get_position().x1+0.01,ax.get_position().y0,0.02,ax.get_position().height])\n",
    "        plt.colorbar(im, cax=cax)\n",
    "        plt.show()\n",
    "\n",
    "        fig = plt.figure(figsize=figsize, **kwargs)\n",
    "        ax = plt.axes()\n",
    "        plt.title('Time')\n",
    "        im = ax.imshow(att_maps[1], cmap=cmap)\n",
    "        cax = fig.add_axes([ax.get_position().x1+0.01,ax.get_position().y0,0.02,ax.get_position().height])\n",
    "        plt.colorbar(im, cax=cax)\n",
    "        plt.show()\n",
    "        \n",
    "        \n",
    "class _XCMPlus_Backbone(Module):\n",
    "    def __init__(self, c_in:int, c_out:int, seq_len:Optional[int]=None, nf:int=128, window_perc:float=1.):\n",
    "        \n",
    "        window_size = int(round(seq_len * window_perc, 0))\n",
    "        self.conv2dblock = nn.Sequential(*[Unsqueeze(1), Conv2d(1, nf, kernel_size=(1, window_size), padding='same'), BatchNorm(nf), nn.ReLU()])\n",
    "        self.conv2d1x1block = nn.Sequential(*[nn.Conv2d(nf, 1, kernel_size=1), nn.ReLU(), Squeeze(1)])\n",
    "        self.conv1dblock = nn.Sequential(*[Conv1d(c_in, nf, kernel_size=window_size, padding='same'), BatchNorm(nf, ndim=1), nn.ReLU()])\n",
    "        self.conv1d1x1block = nn.Sequential(*[nn.Conv1d(nf, 1, kernel_size=1), nn.ReLU()])\n",
    "        self.concat = Concat()\n",
    "        self.conv1d = nn.Sequential(*[Conv1d(c_in + 1, nf, kernel_size=window_size, padding='same'), BatchNorm(nf, ndim=1), nn.ReLU()])\n",
    "            \n",
    "    def forward(self, x):\n",
    "        x1 = self.conv2dblock(x)\n",
    "        x1 = self.conv2d1x1block(x1)\n",
    "        x2 = self.conv1dblock(x)\n",
    "        x2 = self.conv1d1x1block(x2)\n",
    "        out = self.concat((x2, x1))\n",
    "        out = self.conv1d(out)\n",
    "        return out"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "XCMPlus(\n",
       "  (backbone): _XCMPlus_Backbone(\n",
       "    (conv2dblock): Sequential(\n",
       "      (0): Unsqueeze(dim=1)\n",
       "      (1): Conv2dSame(\n",
       "        (conv2d_same): Conv2d(1, 128, kernel_size=(1, 51), stride=(1, 1))\n",
       "      )\n",
       "      (2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
       "      (3): ReLU()\n",
       "    )\n",
       "    (conv2d1x1block): Sequential(\n",
       "      (0): Conv2d(128, 1, kernel_size=(1, 1), stride=(1, 1))\n",
       "      (1): ReLU()\n",
       "      (2): Squeeze(dim=1)\n",
       "    )\n",
       "    (conv1dblock): Sequential(\n",
       "      (0): Conv1d(24, 128, kernel_size=(51,), stride=(1,), padding=(25,))\n",
       "      (1): BatchNorm1d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
       "      (2): ReLU()\n",
       "    )\n",
       "    (conv1d1x1block): Sequential(\n",
       "      (0): Conv1d(128, 1, kernel_size=(1,), stride=(1,))\n",
       "      (1): ReLU()\n",
       "    )\n",
       "    (concat): Concat(dim=1)\n",
       "    (conv1d): Sequential(\n",
       "      (0): Conv1d(25, 128, kernel_size=(51,), stride=(1,), padding=(25,))\n",
       "      (1): BatchNorm1d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
       "      (2): ReLU()\n",
       "    )\n",
       "  )\n",
       "  (head): Sequential(\n",
       "    (0): GAP1d(\n",
       "      (gap): AdaptiveAvgPool1d(output_size=1)\n",
       "      (flatten): Flatten(full=False)\n",
       "    )\n",
       "    (1): LinBnDrop(\n",
       "      (0): Linear(in_features=128, out_features=6, bias=True)\n",
       "    )\n",
       "  )\n",
       ")"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from tsai.data.all import *\n",
    "from tsai.models.XCM import *\n",
    "\n",
    "dsid = 'NATOPS'\n",
    "X, y, splits = get_UCR_data(dsid, split_data=False)\n",
    "tfms = [None, Categorize()]\n",
    "dls = get_ts_dls(X, y, splits=splits, tfms=tfms)\n",
    "model =  XCMPlus(dls.vars, dls.c, dls.len)\n",
    "learn = Learner(dls, model, metrics=accuracy)\n",
    "xb, yb = dls.one_batch()\n",
    "\n",
    "bs, c_in, seq_len = xb.shape\n",
    "c_out = len(np.unique(yb.cpu().numpy()))\n",
    "\n",
    "model = XCMPlus(c_in, c_out, seq_len, fc_dropout=.5)\n",
    "test_eq(model.to(xb.device)(xb).shape, (bs, c_out))\n",
    "model = XCMPlus(c_in, c_out, seq_len, concat_pool=True)\n",
    "test_eq(model.to(xb.device)(xb).shape, (bs, c_out))\n",
    "model = XCMPlus(c_in, c_out, seq_len)\n",
    "test_eq(model.to(xb.device)(xb).shape, (bs, c_out))\n",
    "test_eq(count_parameters(XCMPlus(c_in, c_out, seq_len)), count_parameters(XCM(c_in, c_out, seq_len)))\n",
    "model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/Users/nacho/opt/anaconda3/envs/py36/lib/python3.6/site-packages/torch/nn/modules/module.py:974: UserWarning: Using a non-full backward hook when the forward contains multiple autograd Nodes is deprecated and will be removed in future versions. This hook will be missing some grad_input. Please use register_full_backward_hook to get the documented behavior.\n",
      "  warnings.warn(\"Using a non-full backward hook when the forward contains multiple autograd Nodes \"\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 720x720 with 2 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 720x720 with 2 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "model.show_gradcam(xb[0], yb[0])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "torch.Size([16, 5, 2])\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "create_conv_lin_3d_head(\n",
       "  (0): BatchNorm1d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
       "  (1): Conv1d(128, 5, kernel_size=(1,), stride=(1,), bias=False)\n",
       "  (2): Transpose(-1, -2)\n",
       "  (3): BatchNorm1d(12, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
       "  (4): Transpose(-1, -2)\n",
       "  (5): Linear(in_features=12, out_features=2, bias=False)\n",
       ")"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "bs = 16\n",
    "n_vars = 3\n",
    "seq_len = 12\n",
    "c_out = 10\n",
    "xb = torch.rand(bs, n_vars, seq_len)\n",
    "new_head = partial(conv_lin_3d_head, d=(5, 2))\n",
    "net = XCMPlus(n_vars, c_out, seq_len, custom_head=new_head)\n",
    "print(net.to(xb.device)(xb).shape)\n",
    "net.head"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "torch.Size([16, 2])\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "Sequential(\n",
       "  (0): AdaptiveAvgPool1d(output_size=1)\n",
       "  (1): Flatten(full=False)\n",
       "  (2): BatchNorm1d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
       "  (3): Linear(in_features=128, out_features=512, bias=False)\n",
       "  (4): ReLU(inplace=True)\n",
       "  (5): BatchNorm1d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
       "  (6): Linear(in_features=512, out_features=2, bias=False)\n",
       ")"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "bs = 16\n",
    "n_vars = 3\n",
    "seq_len = 12\n",
    "c_out = 2\n",
    "xb = torch.rand(bs, n_vars, seq_len)\n",
    "net = XCMPlus(n_vars, c_out, seq_len)\n",
    "change_model_head(net, create_pool_plus_head, concat_pool=False)\n",
    "print(net.to(xb.device)(xb).shape)\n",
    "net.head"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/javascript": [
       "IPython.notebook.save_checkpoint();"
      ],
      "text/plain": [
       "<IPython.core.display.Javascript object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "114b_models.XCMPlus.ipynb saved at 2021-11-23 14:56:45.\n",
      "Converted 114b_models.XCMPlus.ipynb.\n",
      "\n",
      "\n",
      "Correct conversion! 😃\n",
      "Total time elapsed 0.083 s\n",
      "Tuesday 23/11/21 14:56:48 CET\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "\n",
       "                <audio  controls=\"controls\" autoplay=\"autoplay\">\n",
       "                    <source src=\"data:audio/wav;base64,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\" type=\"audio/wav\" />\n",
       "                    Your browser does not support the audio element.\n",
       "                </audio>\n",
       "              "
      ],
      "text/plain": [
       "<IPython.lib.display.Audio object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "#hide\n",
    "from tsai.imports import create_scripts\n",
    "from tsai.export import get_nb_name\n",
    "nb_name = get_nb_name()\n",
    "create_scripts(nb_name);"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
